#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <sys/socket.h>

#include "logic.h"
#include "socketinfo.h"
#include "log.h"
#include "struct.h"

#include "lua_function.h"

#include "server_config.h"

#define UNUSED(x) ((void)(x))

static lua_State * L = 0;

static int loadLuaScript()
{
	if (L) lua_close(L);

	L = luaL_newstate();
	if (L) {
		luaL_openlibs(L);
		registe_function(L);
		if(luaL_dofile(L, "logic.lua") != 0) {
			lua_close(L);
			L = 0;
		}
	}
	return L ? 0 : -1;;
}

void process_message(struct connection * conn, const char * data, int cur_len)
{
#if 1
	if ((L == 0) && (loadLuaScript() != 0)) {
		WRITE_ERROR_LOG("loadLuaScript failed");
		bufferevent_free(conn->bev);
		return;
	}

	lua_getglobal(L, "process");
	if (!lua_isfunction(L, -1)) {
		WRITE_WARNING_LOG("process is not a function on script");	
		lua_pop(L, 1);
	} else {
		lua_pushlightuserdata(L, conn);
		lua_pushlstring(L, data, cur_len);
		lua_pcall(L, 2, 0, 0);
	}

	static int message = 0;
	if (++message % 10000 == 0) {
		WRITE_ERROR_LOG("%d message processed", message);
	}
#else
	bufferevent_write(conn->bev, data, cur_len);
	//bufferevent_free(conn->bev);
#endif
}

int read_one_message(struct connection * conn, char * buf, int buf_len)
{
	struct evbuffer * buffer = bufferevent_get_input(conn->bev);
	if (conn->ctx == 0) {
		size_t recv_len = evbuffer_get_length(buffer);
		if (recv_len < sizeof(uint32_t)) {
			return 0;
		}
		uint32_t package_len = 0;
		bufferevent_read(conn->bev, &package_len, sizeof(uint32_t));
		conn->ctx = (void*)(unsigned long)package_len;
	}

	uint32_t package_len = (uint32_t)(unsigned long)conn->ctx;
	if ((package_len + sizeof(uint32_t)) > buf_len) {
		WRITE_WARNING_LOG("socket(id:%llu,fd:%d) "
				"message is too long %u",
				conn->id, conn->fd,
				(unsigned int)package_len);
		return -1;
	}

	size_t recv_len = evbuffer_get_length(buffer);
	if (recv_len  < package_len) {
		return 0;
	}

	memcpy(buf, &package_len,  sizeof(uint32_t));
	bufferevent_read(conn->bev, buf + sizeof(uint32_t), package_len);
	conn->ctx = 0;
	return package_len + sizeof(uint32_t);
}

void logic_on_read(struct connection * conn, void * ctx)
{
	char data[BUFFER_LENGTH];
	do {
		int package_len = read_one_message(conn, data, BUFFER_LENGTH);
		if (package_len < 0) {
			release_connection(conn);
			return;
		}

		if (package_len == 0) {
			break;
		}

		WRITE_WARNING_LOG("socket(id:%llu,fd:%d) "
				"read 1 message, length %u byte(s)",
				conn->id, conn->fd,
				(unsigned int)package_len);
		process_message(conn, data, package_len);
	} while(1);
}

void logic_on_accept(struct connection * conn, void * ctx)
{
	struct timeval read_timeout = {60, 0};
	struct timeval write_timeout = {5, 0};

	int cfg_int = READ_CONFIG_INT("config", "timeout", "read");
	if (cfg_int > 0) {
		read_timeout.tv_sec = cfg_int;
	}

	cfg_int = READ_CONFIG_INT("config", "timeout", "write");
	if (cfg_int > 0) {
		write_timeout.tv_sec = cfg_int;
	}

	bufferevent_set_timeouts(conn->bev, &read_timeout, &write_timeout);
}

void logic_on_closed(struct connection * conn, int reason, void * ctx)
{
}

DELCLARE_LOGIC(
		logic_on_read,
		logic_on_accept,
		logic_on_closed,
		(void*)0
	      );
